/**
 * @brief MCU 定时器 驱动
 * @author 张勇 41204@qq.com
 * @date 2022-04
 */

#include "share/notes_music.h"
#include "mcu.h"
#include "_mcu_clock.h"
#include "_mcu_timer.h"

uint32_t mcu_timer_get_ck_int(mcu_timer_t *dev) {
    switch((uintptr_t)dev->timx) {
        case (uintptr_t)TIM1:
        case (uintptr_t)TIM8:
        case (uintptr_t)TIM9:
        case (uintptr_t)TIM10:
        case (uintptr_t)TIM11: return mcu_clock_get_apb2_freq() * 2;
        case (uintptr_t)TIM2:
        case (uintptr_t)TIM3:
        case (uintptr_t)TIM4:
        case (uintptr_t)TIM5:
        case (uintptr_t)TIM6:
        case (uintptr_t)TIM7:
        case (uintptr_t)TIM12:
        case (uintptr_t)TIM13:
        case (uintptr_t)TIM14: return mcu_clock_get_apb1_freq() * 2;
        default: return 0;
    }
}

uint32_t mcu_timer_get_update_irqn(mcu_timer_t *dev) {
    switch((uintptr_t)dev->timx) {
        case (uintptr_t)TIM1:  return TIM1_UP_TIM10_IRQn;
        case (uintptr_t)TIM2:  return TIM2_IRQn;
        case (uintptr_t)TIM3:  return TIM3_IRQn;
        case (uintptr_t)TIM4:  return TIM4_IRQn;
        case (uintptr_t)TIM5:  return TIM5_IRQn;
        case (uintptr_t)TIM6:  return TIM6_DAC_IRQn;
        case (uintptr_t)TIM7:  return TIM7_IRQn;
        case (uintptr_t)TIM8:  return TIM8_UP_TIM13_IRQn;
        case (uintptr_t)TIM9:  return TIM1_BRK_TIM9_IRQn;
        case (uintptr_t)TIM10: return TIM1_UP_TIM10_IRQn;
        case (uintptr_t)TIM11: return TIM1_TRG_COM_TIM11_IRQn;
        case (uintptr_t)TIM12: return TIM8_BRK_TIM12_IRQn;
        case (uintptr_t)TIM13: return TIM8_UP_TIM13_IRQn;
        case (uintptr_t)TIM14: return TIM8_TRG_COM_TIM14_IRQn;
        default: return 10000; // STM32 中很多中断号是负值，所以只好返回一个很大的值表示无效值 !!!
    }
}

std_err_t mcu_timer_init(mcu_timer_t *dev, mcu_timer_clksrc_t clksrc, void *clksrc_opt, uint32_t freq_main, uint32_t freq_mod) {
    if(!dev || !dev->timx || clksrc >= MCU_TIMER_CLKSRC_MAX || !freq_main || !freq_mod || freq_mod > freq_main) return STD_ERR_INVALID_ARG;
    uint32_t prescaler = 0;

    if(clksrc == MCU_TIMER_CLKSRC_INT) {
        // 获取 Timer 的 CK_INT 时钟频率(即所在的 APB 总线时钟频率的 1/2 倍频)
        uint32_t ck_int = mcu_timer_get_ck_int(dev);
        // 要设置的主频率不能超过 CK_INT 时钟频率
        if(freq_main > ck_int) return STD_ERR_OUT_OF_RANGE;
        // 假设 CK_INT 为 84MHz，分频后为 84MHz/(83+1) = 1MHz
        prescaler = (ck_int / freq_main) - 1;
    }

    // 设置 Timer 预分频器
    LL_TIM_SetPrescaler(dev->timx, prescaler);
    // 设置自动重装载值 - 决定 PWM 频率 (1MHz/1000 = 1kHz PWM 频率)
    LL_TIM_SetAutoReload(dev->timx, (freq_main / freq_mod - 1)); // ARR = 999
    // 计算多长时间会 update 一次
    dev->update_period_us = 1000000UL / freq_mod;
    dev->freq_main = freq_main;
    dev->freq_mod = freq_mod;
	return STD_ERR_OK;
}

std_err_t mcu_timer_set_freq_mod(mcu_timer_t *dev, uint32_t freq_mod) {
    if(!dev || !freq_mod || freq_mod > dev->freq_main) return STD_ERR_INVALID_ARG;
    
    // 改变 Timer 的调制频率(通过 ARR 控制)
    LL_TIM_SetAutoReload(dev->timx, (dev->freq_main / freq_mod - 1));
    dev->freq_mod = freq_mod;
    
    // 注意: Timer 的调制频率改变了，各个子通道的 duty 要重新设置 !!!
    for(int i=0; i<4; i++) {
        if(dev->chs[i]) mcu_timer_ch_oc_set_duty(dev->chs[i], dev->chs[i]->duty);
    }
	return STD_ERR_OK;
}

std_err_t mcu_timer_start(mcu_timer_t *dev) {
    // 使能 自动重装载预加载
    LL_TIM_EnableARRPreload(dev->timx);
    // 使能 计数器
    LL_TIM_EnableCounter(dev->timx);
    // 使能 主输出
    LL_TIM_EnableAllOutputs(dev->timx);
	return STD_ERR_OK;
}

std_err_t mcu_timer_stop(mcu_timer_t *dev) {
    // 禁用 主输出
    LL_TIM_DisableAllOutputs(dev->timx);
    // 禁用 计数器
    LL_TIM_DisableCounter(dev->timx);
    // 禁用 自动重装载预加载
    LL_TIM_DisableARRPreload(dev->timx);
	return STD_ERR_OK;
}

// chidx(1~4) 转换为 LL_TIM_CHANNEL_CHx
static const uint32_t _ll_tim_chx[] = { LL_TIM_CHANNEL_CH1, LL_TIM_CHANNEL_CH2, LL_TIM_CHANNEL_CH3, LL_TIM_CHANNEL_CH4 };
#define IDX2LLCHX(idx) (_ll_tim_chx[idx - 1])

std_err_t mcu_timer_ch_init(mcu_timer_ch_t *ch) {
    if(!ch || !ch->parent || !ch->chpin_mux_init) return STD_ERR_INVALID_ARG;

    // 配置引脚复用关系
    mcu_gpio_num_t chpin = ch->chpin_mux_init();
    if(chpin >= MCU_GPIO_NUM_MAX) return STD_ERR_NO_RESOURCES;

    // 初始化引脚 GPIO
	GPIO_TypeDef* port = mcu_gpio_port(chpin);
	uint32_t pin = mcu_gpio_pin(chpin);
    LL_GPIO_SetPinMode      (port, pin, LL_GPIO_MODE_ALTERNATE);
    LL_GPIO_SetPinSpeed     (port, pin, LL_GPIO_SPEED_FREQ_HIGH);
    LL_GPIO_SetPinOutputType(port, pin, LL_GPIO_OUTPUT_PUSHPULL);

    // 配置 PWM 模式1
    LL_TIM_OC_SetMode(ch->parent->timx, IDX2LLCHX(ch->chidx), LL_TIM_OCMODE_PWM1);
    // 设置通道引脚极性
    LL_TIM_OC_SetPolarity(ch->parent->timx, IDX2LLCHX(ch->chidx), ch->active_level ? LL_TIM_OCPOLARITY_HIGH : LL_TIM_OCPOLARITY_LOW);

    // 在 Timer 中保存其子通道的指针
    ch->parent->chs[ch->chidx - 1] = ch;
    return STD_ERR_OK;
}

_RAMCODE_ void mcu_timer_update_isr_stub(mcu_timer_t *dev) {
    // 因为中断可能是共享的，所以需要通过 中断标志位 判断 是否真的发生了 update 中断
    if(!LL_TIM_IsActiveFlag_UPDATE(dev->timx)) return;
    
    // 调用 Timer 的 update 中断处理
    if(dev->update_handler) dev->update_handler(dev);
    
    // 调用 Timer 各个子通道的 update 中断处理
    for(int i=0; i<4; i++) {
        if(dev->chs[i] && dev->chs[i]->update_handler) dev->chs[i]->update_handler(dev->chs[i]);
    }
    
    // 清除中断标志位
    LL_TIM_ClearFlag_UPDATE(dev->timx);
}

std_err_t mcu_timer_enable_update_irq(mcu_timer_t *dev) {
    LL_TIM_EnableIT_UPDATE(dev->timx);
    return mcu_intc_enable(0, mcu_timer_get_update_irqn(dev), true);
}

std_err_t mcu_timer_disable_update_irq(mcu_timer_t *dev) {
    LL_TIM_DisableIT_UPDATE(dev->timx);
    return STD_ERR_OK;
}

std_err_t mcu_timer_ch_oc_set_compare(mcu_timer_ch_t *ch, uint32_t cnt) {
    switch(ch->chidx) {
        case 1: LL_TIM_OC_SetCompareCH1(ch->parent->timx, cnt); break;
        case 2: LL_TIM_OC_SetCompareCH2(ch->parent->timx, cnt); break;
        case 3: LL_TIM_OC_SetCompareCH3(ch->parent->timx, cnt); break;
        case 4: LL_TIM_OC_SetCompareCH4(ch->parent->timx, cnt); break;
        default: return STD_ERR_INVALID_ARG;
    }
    return STD_ERR_OK;
}

uint32_t mcu_timer_ch_oc_get_compare(mcu_timer_ch_t *ch) {
    switch(ch->chidx) {
        case 1: return LL_TIM_OC_GetCompareCH1(ch->parent->timx);
        case 2: return LL_TIM_OC_GetCompareCH2(ch->parent->timx);
        case 3: return LL_TIM_OC_GetCompareCH3(ch->parent->timx);
        case 4: return LL_TIM_OC_GetCompareCH4(ch->parent->timx);
        default: return 0;
    }
}

std_err_t mcu_timer_ch_oc_set_duty(mcu_timer_ch_t *ch, uint32_t duty) {
    if(!ch || !ch->parent || duty > 10000) return STD_ERR_INVALID_ARG;
    uint32_t cnt = LL_TIM_GetAutoReload(ch->parent->timx); // Timer 的自动装载计数值
    cnt = cnt * duty / 10000U;
    ch->duty = duty;
    return mcu_timer_ch_oc_set_compare(ch, cnt);
}

static void _timer_ch_update_isr(mcu_timer_ch_t *ch) {
    if(ch->duration_us > ch->parent->update_period_us) {
        ch->duration_us -= ch->parent->update_period_us;
    } else if(ch->duration_us) {
        if(note_validate(ch->notes)) {
            // 还有未播放完成的音符，继续播放它
            mcu_timer_set_freq_mod(ch->parent, note_freq_hz(ch->notes));        // 修改 Timer 的调制频率
            ch->duration_us = note_time_ms(ch->notes, ch->note_speed) * 1000;   // 设置持续时间为 节拍时间
            // 指向下一个音符
            ch->notes = note_next(ch->notes);
        } else {
            // 停止播放
            mcu_timer_ch_oc_stop(ch);
        }
    }
}

std_err_t mcu_timer_ch_oc_start(mcu_timer_ch_t *ch, unios_us_t duration_us) {
    // 设置持续时间
    ch->duration_us = duration_us;
    if(duration_us) {
        ch->update_handler = _timer_ch_update_isr;
        mcu_timer_enable_update_irq(ch->parent);
    }
    // 使能输出比较预加载
    LL_TIM_OC_EnablePreload(ch->parent->timx, IDX2LLCHX(ch->chidx));
    // 使能输出比较通道
    LL_TIM_CC_EnableChannel(ch->parent->timx, IDX2LLCHX(ch->chidx));
    return STD_ERR_OK;
}

std_err_t mcu_timer_ch_oc_stop(mcu_timer_ch_t *ch) {
    // 禁用比较通道
    LL_TIM_CC_DisableChannel(ch->parent->timx, IDX2LLCHX(ch->chidx));
    // 禁用输出比较预加载
    LL_TIM_OC_DisablePreload(ch->parent->timx, IDX2LLCHX(ch->chidx));
    // 清除状态
    ch->duration_us = 0;
    ch->notes = NULL;
    return STD_ERR_OK;
}

std_err_t mcu_timer_ch_oc_play_notes(mcu_timer_ch_t *ch, const Note *notes, uint32_t speed) {
    if(!ch || !notes || !note_validate(notes)) return STD_ERR_INVALID_ARG;
    
    // 播放第一个音符
    mcu_timer_set_freq_mod(ch->parent, note_freq_hz(notes));                // 修改 Timer 的调制频率
    mcu_timer_ch_oc_set_duty(ch, 5000);                                     // 设置 PWM 波形输出占空比为 50%
    mcu_timer_ch_oc_start(ch, note_time_ms(notes, ch->note_speed) * 1000);  // 启动 PWM 波形输出，并指定持续时间为 节拍时间
    
    // 指向下一个音符
    ch->notes = note_next(notes);
    return STD_ERR_OK;
}
